<?php

namespace App\Services\AetherUpload;

use App\Services\AetherUpload\Lib\ConfigMapper;
use App\Services\AetherUpload\Lib\Message;
use App\Services\AetherUpload\Lib\PartialResource;
use App\Services\AetherUpload\Lib\Resource;
use App\Services\AetherUpload\Lib\Responser;
use App\Services\AetherUpload\Lib\Util;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Str;

class AetherUploadService
{
    /**
     * @param $resourceName
     * @param $resourceSize
     * @param $resourceHash
     * @param $group
     * @return JsonResponse
     */
    public function preprocess($resourceName, $resourceSize, $resourceHash, $group)
    {
        $result = [
            'error'                => 0,
            'chunkSize'            => 0,
            'groupSubDir'          => '',
            'resourceTempBaseName' => '',
            'resourceExt'          => '',
            'savedPath'            => '',
        ];

        try {

            ConfigMapper::instance()->applyGroupConfig($group);

            $result['resourceTempBaseName'] = $resourceTempBaseName = Util::generateTempName();
            $result['resourceExt'] = $resourceExt = strtolower(pathinfo($resourceName, PATHINFO_EXTENSION));
            $result['groupSubDir'] = $groupSubDir = Util::generateSubDirName();
            $result['chunkSize'] = ConfigMapper::get('chunk_size');

            $partialResource = new PartialResource($resourceTempBaseName, $resourceExt, $groupSubDir);

            $partialResource->filterBySize($resourceSize);

            $partialResource->filterByExtension($resourceExt);

            // $partialResource->create();

            $partialResource->chunkIndex = 0;

        } catch (\Exception $e) {

            return Responser::reportError($result, $e->getMessage());
        }

        return Responser::returnResult($result);
    }

    /**
     * @param $chunkTotalCount
     * @param $chunkIndex
     * @param $resourceTempBaseName
     * @param $resourceExt
     * @param $chunk
     * @param $groupSubDir
     * @param $resourceHash
     * @param $group
     * @return JsonResponse
     * @throws \Exception
     */
    public function saveChunk($chunkTotalCount, $chunkIndex, $resourceTempBaseName, $resourceExt, $chunk, $groupSubDir, $resourceHash, $group)
    {
        $partialResource = null;

        $result = [
            'error'     => 0,
            'savedPath' => '',
        ];

        try {

            if ($chunk->getError() > 0) {
                throw new \Exception(Message::message('upload_error'));
            }

            if ($chunk->isValid() === false) {
                throw new \Exception(Message::message('http_post_only'));
            }

            ConfigMapper::instance()->applyGroupConfig($group);

            $partialResource = new PartialResource($resourceTempBaseName, $resourceExt, $groupSubDir);

            // do a check to prevent security intrusions
            if ($chunkIndex > 1 && $partialResource->exists() === false) {
                throw new \Exception(Message::message('invalid_operation'));
            }

            // validate the data in header file to avoid the errors when network issue occurs
            if ((int)($partialResource->chunkIndex) !== (int)$chunkIndex - 1) {
                return Responser::returnResult($result);
            }

            $partialResource->append($chunk->getRealPath(), $chunkIndex);

            $partialResource->chunkIndex = $chunkIndex;

            // determine if the resource file is completed
            if ($chunkIndex === $chunkTotalCount) {

                $partialResource->checkSize();

                $partialResource->checkMimeType();

                // trigger the event before an upload completes
                if (!empty($beforeUploadCompleteEvent = ConfigMapper::get('event_before_upload_complete'))) {
                    event(new $beforeUploadCompleteEvent($partialResource));
                }

                $resourceRealHash = $partialResource->calculateHash();

                if (ConfigMapper::get('lax_mode') === false && $resourceHash !== $resourceRealHash) {
                    throw new \Exception(Message::message('upload_error'));
                }

                $completeName = Util::getFileName($resourceRealHash, $resourceExt);
                //唯一性 防止重复
                if (ConfigMapper::get('use_unique_name') && $partialResource->hasCompleteName($completeName)) {
                    $completeName = Util::getFileName(Str::random(32), $resourceExt);
                }

                $savedPath = $partialResource->rename($completeName);

                unset($partialResource->chunkIndex);

                // trigger the event when an upload completes
                if (!empty($uploadCompleteEvent = ConfigMapper::get('event_upload_complete'))) {
                    event(new $uploadCompleteEvent(new Resource($group, ConfigMapper::get('group_dir'), $groupSubDir, $completeName)));
                }

                $result['savedPath'] = $savedPath;

            }

        } catch (\Exception $e) {

            $partialResource->delete();

            unset($partialResource->chunkIndex);

            return Responser::reportError($result, $e->getMessage());
        }

        return Responser::returnResult($result);

    }

}
